#include "paintCursor.h"

PaintCursor::PaintCursor() {

   size = 0.02;
   number_points = 8;

   shader_cursor = new glslKernel();
   shader_cursor->fragment_source("../paint/shader_cursor.frag");
   shader_cursor->install(true);
   shader_cursor->use(false);

   clicked = false;
   color = Color(1.0, 0.0, 0.0, 1.0);
}

PaintCursor::PaintCursor(GLint x, GLint y) {
   PaintCursor();
   sPosition[0] = x;
   sPosition[1] = y;
}

PaintCursor::~PaintCursor() {
}

void PaintCursor::updatePosition(GLint x, GLint y){
   sPosition[0] = x;
   sPosition[1] = y;

   GLint viewport[4];
   GLdouble pMtx[16], mMtx[16];

   glGetIntegerv(GL_VIEWPORT, viewport);
   glGetDoublev(GL_PROJECTION_MATRIX, pMtx);
   glGetDoublev(GL_MODELVIEW_MATRIX, mMtx);
   gluUnProject(sPosition[0], sPosition[1], -0.001, mMtx, pMtx, viewport, &position[0], &position[1], &position[2]);
}

void PaintCursor::update() {
   Color pdata = getNormal(sPosition[0], sPosition[1]);

//   pdata[0] = 1;
//   pdata[1] = 1;
//   pdata[2] = 1;

   glMatrixMode(GL_PROJECTION);
   glLoadIdentity();
   glOrtho(-1, 1, -1, 1, -4, 4);
   glMatrixMode(GL_MODELVIEW);
   glLoadIdentity();

   Vector N(pdata[0], pdata[1], pdata[2]);
   Vector P0(position[0], position[1], position[2]);
   if (N == Vector(1.0, 1.0, 1.0) || N == Vector(0.0, 0.0, 0.0)) {
      N = Vector(0.0, 0.0, 1.0);
   }
   N.normalize();
   normal = N;

   Vector v(0.0, 0.0, 1.0);

   double ang = acos(v * N) * (180 / PI);
   Vector r = v ^ N; // cross product

   GLint viewport[4];
   GLdouble pMtx[16], mMtx[16];

   glPushMatrix();
   glTranslatef(P0[0], P0[1], 0.0);
   glRotatef(ang, r[0], r[1], r[2]);
   glTranslatef(-P0[0], -P0[1], 0.0);
   glGetIntegerv(GL_VIEWPORT, viewport);
   glGetDoublev(GL_PROJECTION_MATRIX, pMtx);
   glGetDoublev(GL_MODELVIEW_MATRIX, mMtx);
   glPopMatrix();

   points.clear();
   ipoints.clear();
   for (int i = 0; i < number_points; i ++) {
      Point p;
      p[0] = P0[0] + size * cos(PI / 4 + 2 * i * PI / number_points);
      p[1] = P0[1] + size * sin(PI / 4 + 2 * i * PI / number_points);
      p[2] = 0.0;
      p = MatrixMult(mMtx, p);
      points.push_back(p);
      Point p2;
      gluProject(p[0], p[1], p[2], pMtx, mMtx, viewport, &p2[0], &p2[1], &p2[2]);
      iPoint ip((int) p2[0], (int) p2[1], (int) p2[2]);
      ipoints.push_back(ip);
   }
}

void PaintCursor::draw(){

   glMatrixMode(GL_PROJECTION);
   glLoadIdentity();
   glOrtho(-1, 1, -1, 1, -4, 4);
   glMatrixMode(GL_MODELVIEW);
   glLoadIdentity();

//   shader_cursor->use();
//   shader_cursor->set_uniform("normal_cursor", color[0], color[1], color[2], color[3]);

   Vector N = normal;
   Vector P0(position[0], position[1], 0.0);
   Vector v(0.0, 0.0, 1.0);
//   Vector r = v ^ N; // cross product
   glColor4f(1.0 - color[0], 1.0 - color[1], 1.0 - color[2], 1.0);
   glBegin(GL_LINE_LOOP);
   for (int i = 0; i < number_points; i ++) {
      Point p = points[i];
      glVertex3f(p[0], p[1], p[2]);
   }
   glEnd();

   glBegin(GL_LINES);
      glVertex3f(P0[0], P0[1], P0[2]);
      glVertex3f(P0[0] + N[0] * size, P0[1] + N[1] * size, P0[2] + N[2] * size);
//      glVertex3f(P0[0] + r[0] * size, P0[1] + r[1] * size, P0[2] + r[2] * size);
   glEnd();

//   shader_cursor->use(false);
}

void PaintCursor::in(){
   if (size < 0.4) size = size + 0.01;
}

void PaintCursor::out(){
   if (size > 0.0) size = size - 0.01;
}


void PaintCursor::increase(){
   if (number_points < 32) number_points += 1;
}

void PaintCursor::decrease(){
   if (number_points > 3) number_points -= 1;
}

Point PaintCursor::MatrixMult(GLdouble m[16], Point p) {
   return Point(
         m[0] * p[0] + m[4] * p[1] + m[8] * p[2] + m[12],
         m[1] * p[0] + m[5] * p[1] + m[9] * p[2] + m[13],
         m[2] * p[0] + m[6] * p[1] + m[10] * p[2] + m[14]);
}

Color PaintCursor::getNormal(int x, int y) {
   Color result(2 * normalBuffer[ (x + nbw * y) * 4] - 1,
                2 * normalBuffer[ (x + nbw * y) * 4 + 1] - 1,
                2 * normalBuffer[ (x + nbw * y) * 4 + 2] - 1,
                normalBuffer[(x + nbw * y) * 4 + 3]);
   return result;
}

void PaintCursor::setCamera(Camera* c) {
   camera = c;
   ambi = camera->ambientLight();
   diff = camera->diffuseLight();
   spec = camera->specularLight();
   lpos = camera->lightVector();
}
